#pragma once

#include <iostream>
#include <sys/resource.h>
#include "../common/util.hpp"
#include "../common/log.hpp"

class Run_Modle {
        //设置进程资源限制
        static void Set_Proc_limit(int cpu_limit,int mem_limit)
        {
            //设置cpu限定时长
            struct rlimit cpu_rlim;
            cpu_rlim.rlim_cur = cpu_limit;
            cpu_rlim.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_CPU , &cpu_rlim);
            //设置逻辑地址空间限定大小
            struct rlimit mem_rlim;
            mem_rlim.rlim_cur = mem_limit;
            mem_rlim.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_AS  , &mem_rlim);
        }
    public:
        //只考虑是否能跑完代码，至于结果对错是由测试用例决定，交给上层模块处理
        // 返回值 > 0: 程序异常了，退出时收到了信号，返回值就是对应的信号编号\
         * 返回值 == 0: 正常运行完毕的，结果保存到了对应的临时文件中\
         * 返回值 < 0: 内部错误
        static int Run(const std::string &filename,int cpu_limit,int mem_limit)
        {
            std::string _exe = Util_File::ExtensionObj(filename);//获取可执行文件名
            std::string _stdin = Util_File::Stdindup(filename);//获取重定向标准输入文件名
            std::string _stdout = Util_File::Stdoutdup(filename);//获取重定向标准输出文件名
            std::string _stderr = Util_File::Stderrdup(filename);//获取重定向标准错误文件名
            
            umask(0);//设置文件权限掩码
            int _stdin_fd = open(_stdin.c_str(), O_RDONLY | O_CREAT, 0644);
            int _stdout_fd = open(_stdout.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
            int _stderr_fd = open(_stderr.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
            if(_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0){
                LOG__ERR("open file error");
                return -1;//打开文件失败
            }
            
            pid_t pid = fork();
            if(pid == 0){//子进程
                Set_Proc_limit(cpu_limit,mem_limit);//设置资源限制
                //设置重定向
                dup2(_stdin_fd, 0);
                dup2(_stdout_fd, 1);
                dup2(_stderr_fd, 2);
                execl(_exe.c_str(), _exe.c_str(), nullptr);//运行可执行文件(进行程序替换)
                
                LOG__ERR("execl error");
                exit(1);//运行失败

            }else if(pid > 0){//父进程
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                int status = 0;
                waitpid(pid, &status, 0);//等待子进程结束
                LOG__INF("program exit with status %d",(status & 0x7f));
                return status & (0x7f);//返回子进程退出状态
            }else{
                LOG__ERR("fork error");
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                return -2;//fork失败
            }
            
        }
};